// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_APP_LIST_SEARCH_MIXER_H_
#define UI_APP_LIST_SEARCH_MIXER_H_

#include <stddef.h>

#include <vector>

#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/scoped_vector.h"
#include "ui/app_list/app_list_export.h"
#include "ui/app_list/app_list_model.h"
#include "ui/app_list/search/history_types.h"

namespace app_list {

namespace test {
    FORWARD_DECLARE_TEST(MixerTest, Publish);
}

class SearchProvider;
class SearchResult;

// Mixer collects results from providers, sorts them and publishes them to the
// SearchResults UI model. The targeted results have 6 slots to hold the
// result. The search controller can specify any number of groups, each with a
// different number of results and priority boost. The "omnibox" group is
// expected to contain omnibox results, and will be treated specially.
class APP_LIST_EXPORT Mixer {
public:
    explicit Mixer(AppListModel::SearchResults* ui_results);
    ~Mixer();

    // Adds a new mixer group. A maximum of |max_results| results will be
    // displayed from this group (if 0, will allow unlimited results from this
    // group). Behaviour depends on the AppListMixer field trial:
    // - If default: Each result in the group will have its score boosted by
    //   |boost|. |multiplier| is ignored.
    // - If "Blended": |max_results| is a "soft" maximum; if there aren't enough
    //   results from all groups, more than |max_results| may be chosen from this
    //   group. Each result in the group will have its score multiplied by
    //   |multiplier|. |boost| is ignored.
    // Returns the group's group_id.
    size_t AddGroup(size_t max_results, double boost, double multiplier);

    // Adds a new mixer group for the special "omnibox" group. This group will be
    // treated specially by the Mixer (it will be truncated such that it fills the
    // remaining slots without overflowing, but with at least one result). A
    // maximum of one group should be added using this method.
    size_t AddOmniboxGroup(size_t max_results, double boost, double multiplier);

    // Associates a provider with a mixer group.
    void AddProviderToGroup(size_t group_id, SearchProvider* provider);

    // Collects the results, sorts and publishes them.
    void MixAndPublish(bool is_voice_query, const KnownResults& known_results);

private:
    FRIEND_TEST_ALL_PREFIXES(test::MixerTest, Publish);

    // Used for sorting and mixing results.
    struct APP_LIST_EXPORT SortData {
        SortData();
        SortData(SearchResult* result, double score);

        bool operator<(const SortData& other) const;

        SearchResult* result; // Not owned.
        double score;
    };
    typedef std::vector<Mixer::SortData> SortedResults;

    class Group;
    typedef ScopedVector<Group> Groups;

    // Publishes the given |new_results| to |ui_results|, deleting any existing
    // results that are not in |new_results|. Results that already exist in
    // |ui_results| are reused to avoid flickering caused by icon reload.
    static void Publish(const SortedResults& results,
        AppListModel::SearchResults* ui_results);

    // Removes entries from |results| with duplicate IDs. When two or more results
    // have the same ID, the earliest one in the |results| list is kept.
    // NOTE: This is not necessarily the one with the highest *score*, as
    // |results| may not have been sorted yet.
    static void RemoveDuplicates(SortedResults* results);

    void FetchResults(bool is_voice_query, const KnownResults& known_results);

    AppListModel::SearchResults* ui_results_; // Not owned.
    Groups groups_;

    // The ID of the omnibox group. The group with this ID will be treated
    // specially by the Mixer. Ignored if the AppListMixer field trial is
    // "Blended".
    // TODO(mgiuca): Remove this after the field trial is complete.
    size_t omnibox_group_ = 0;
    // Whether |omnibox_group_| has been set.
    bool has_omnibox_group_ = false;

    DISALLOW_COPY_AND_ASSIGN(Mixer);
};

} // namespace app_list

#endif // UI_APP_LIST_SEARCH_MIXER_H_
